package com.bajiao.app.aspect;

import cn.hutool.core.util.StrUtil;
import com.bajiao.app.base.AppBaseController;
import com.bajiao.common.aspect.annotation.Idempotent;
import com.bajiao.common.util.DateUtils;
import com.bajiao.common.util.SpelUtil;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;

/**
 * @Description  接口幂等性校验
 * @author FanJq
 */
@Aspect
@Component
public class IdempotentAspect extends AppBaseController {

	@Resource
	private RedissonClient redissonClient;

	private static final String REDISSON_LOCK_PREFIX = "idempotent_lock:";

	@Around("@annotation(idempotentLock)")
	public Object around(ProceedingJoinPoint joinPoint, Idempotent idempotentLock) throws Throwable {

		HttpServletRequest request= ((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest();
		String userId = getUserId(request);
		userId = StrUtil.isBlank(userId) ? "000" : userId;
		String spel = idempotentLock.key();
		String lockName = idempotentLock.lockName() + userId;
		RLock rLock = redissonClient.getLock(getRedisKey(joinPoint,lockName,spel));
		Object result = null;
		rLock.tryLock(idempotentLock.expire(), idempotentLock.expire(), idempotentLock.timeUnit());
		try {
			rLock.lock(idempotentLock.expire(),idempotentLock.timeUnit());
			//执行方法
			result = joinPoint.proceed();

		} finally {
			if (rLock.isLocked()) {
				rLock.unlock();
			}
		}
		return result;
	}

	/**
	 * 将spel表达式转换为字符串
	 * @param joinPoint 切点
	 * @return redisKey
	 */
	private String getRedisKey(ProceedingJoinPoint joinPoint,String lockName,String spel) {
		Signature signature = joinPoint.getSignature();
		MethodSignature methodSignature = (MethodSignature) signature;
		Method targetMethod = methodSignature.getMethod();
		Object target = joinPoint.getTarget();
		Object[] arguments = joinPoint.getArgs();
		return REDISSON_LOCK_PREFIX + lockName + StrUtil.COLON + SpelUtil.parse(target,spel, targetMethod, arguments);
	}
}
